package com.lifeonwalden.ms.web.msgConverter;

import com.lifeonwalden.ms.common.logger.LoggerUtil;
import com.lifeonwalden.ms.web.view.HtmlView;
import com.lifeonwalden.ms.web.view.SystemViewFreemarkerService;
import freemarker.template.Template;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.FormattedMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class HtmlViewHttpMessageConverter implements HttpMessageConverter<HtmlView> {
    private final static Logger logger = LoggerUtil.getLogger(HtmlViewHttpMessageConverter.class);

    public Charset defaultCharset = StandardCharsets.UTF_8;

    @Autowired
    private SystemViewFreemarkerService freemarkerService;

    private static String ERROR_404 = "<html><body>404</body></html>";

    private static String ERROR_500 = "<html><body>500</body></html>";

    @Override
    public List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
        if (HtmlView.class == clazz) {
            return getSupportedMediaTypes();
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return HtmlView.class == clazz;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return Arrays.asList(MediaType.TEXT_HTML);
    }

    @Override
    public HtmlView read(Class<? extends HtmlView> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        throw new HttpMessageNotReadableException("Only support render html to response", inputMessage);
    }

    @Override
    public void write(HtmlView htmlView, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(4096);
        Template template = null;

        try {
            template = freemarkerService.fetch(htmlView.getPath());
        } catch (Throwable e) {
            logger.error(new FormattedMessage("Not found template : {}", htmlView.getPath()), e);

            byteArrayOutputStream.reset();
            byteArrayOutputStream.write(ERROR_404.getBytes(getCharset(htmlView)));
        }

        if (null != template) {
            try {
                template.process(htmlView.getDataModel(), new OutputStreamWriter(byteArrayOutputStream));
            } catch (Throwable e) {
                logger.error(new FormattedMessage("Failed to process template : {}", htmlView.getPath()), e);

                byteArrayOutputStream.reset();
                byteArrayOutputStream.write(ERROR_500.getBytes(getCharset(htmlView)));
            }
        }

        HttpHeaders headers = outputMessage.getHeaders();
        if (headers.getContentLength() < 0) {
            outputMessage.getHeaders().setContentLength(byteArrayOutputStream.size());
        }
        if (null == headers.getContentType()) {
            headers.setContentType(MediaType.parseMediaType("text/html;charset=".concat(getCharset(htmlView).name())));
        }
        byteArrayOutputStream.writeTo(outputMessage.getBody());
        outputMessage.getBody().flush();
    }

    public Charset getCharset(HtmlView htmlView) {
        Charset charset = defaultCharset;
        if (null != htmlView.getCharset()) {
            charset = htmlView.getCharset();
        }

        return charset;
    }
}
